import {
    isEmpty as lodashIsEmpty,
    isNumber,
    unset,
    defaults,
    pickBy,
    cloneDeep,
    forEach,
    set,
    pick,
    get,
    isBoolean,
    isFunction,
    forIn,
    escapeRegExp,
    filter,
    sortBy,
    find,
    isDate,
    isPlainObject,
    isString,
    eq
} from 'lodash';
import { construct, destruct } from '@aximario/json-tree';

/**
 * 数组交换位置
 *
 * @export
 * @param {*} arr 数组
 * @param {*} oldIndex 旧位置
 * @param {*} newIndex 新位置
 */
export function swapArray(arr, oldIndex, newIndex) {
    const page = arr[oldIndex];
    arr.splice(oldIndex, 1);
    arr.splice(newIndex, 0, page);
}

/**
 * todo 需要优化分解
 * 循环遍历处理树形数据
 *
 * @param {*} data 数据源
 * @param {Function} callback 遍历方法
 * @param {string} [childName='children'] 子节点名称
 * @param {number} [level=1] 给每个节点添加层级字段，默认从1开始
 */
export function eachTree(
    data: any,
    callback?: Function,
    childName: string = 'children',
    level: number = 1,
    startIndex = 1,
    stripeIndex: number = 1
) {
    let current: any, children: any, dataStripeIndex;
    // console.log(data);
    for (let i = 0, len = data.length; i < len; i++) {
        current = data[i];
        // 添加层级
        data[i].level = level;
        if (level == startIndex) {
            dataStripeIndex = i;
        } else if (level > startIndex) {
            dataStripeIndex = stripeIndex;
        }
        data[i].stripeIndex = dataStripeIndex;
        children = [];
        if (current[childName] && current[childName].length > 0) {
            children = current[childName];
        }
        callback && callback(current, children);
        if (children.length > 0) {
            eachTree(
                children,
                callback,
                childName,
                level + 1,
                startIndex,
                dataStripeIndex
            );
        }
    }
}

/**
 * todo 是否可以用findKey来查找对应的元素位置 然后取值
 * 如果需要调整值的话用set来调整
 *
 * @param {*} data 数据源
 * @param {Function} callback 查询条件{a:1,b:2}或者查询方法
 * @param {string} [childName='children'] 子节点字段名称
 */
export function findTree(
    data: any,
    queryData: any,
    childName: string = 'children'
) {
    // 根据条件查询数据
    let result = find(data, queryData);
    if (!result) {
        // 如果查询不到数据，遍历子节点
        forEach(data, (item) => {
            const list = item[childName];
            if (!isEmpty(list)) {
                // 如果子节点中查到数据，结束循环
                result = findTree(list, queryData, childName);
                if (result) {
                    return false;
                }
            }
        });
    }
    return result;
}

/**
 * 根据查询调整模糊查询树形数据
 *
 * @param {*} treeData 数据源
 * @param {*} queryData 查询条件{a:1,b:2}或者查询方法
 * @param {*} [{
 *         id = 'id',
 *         pid = 'parentId',
 *         children = 'children',
 *         orderBy 排序方式
 *     }={}]
 */
export function queryTree(
    treeData: any,
    queryData: any,
    { id = 'id', pid = 'parentId', children = 'children', orderBy }: any = {}
) {
    let queryFun: any;
    if (isFunction(queryData)) {
        queryFun = queryData;
    } else if (!isEmpty(queryData)) {
        // 清除查询条件中的空数据
        queryData = clearObject(cloneDeep(queryData));
        // 根据查询条件生成模糊查询正则表达式
        forIn(queryData, (value: any, key: any) => {
            queryData[key] = new RegExp(escapeRegExp(value), 'i');
        });
        queryFun = function(item: any) {
            let is = true;
            forIn(queryData, (value: any, key: any) => {
                if (!value.test(item[key])) {
                    is = false;
                }
            });
            return is;
        };
    }
    // 如果查询方法存在
    if (queryFun && !isEmpty(treeData)) {
        // 深拷贝
        treeData = cloneDeep(treeData);
        // 将树形数据转为扁平数据
        treeData = destruct(treeData, {
            id,
            pid,
            children
        });
        // 根据查询条件过滤数据
        treeData = filter(treeData, queryFun);
        // 如果有排序字段，重新排序
        if (isEmpty(orderBy)) {
            treeData = sortBy(treeData, orderBy);
        }
        // 再将扁平数据转换为树形数据
        treeData = construct(treeData, {
            id,
            pid,
            children
        });
    }
    return treeData;
}

/**
 * 判断是否为空对象，空字符串，null
 * {},[],'',null会返回true
 * @export
 * @param {any} v
 * @return {*}  {boolean}
 */
export function isEmpty(v: any): boolean {
    if (isNumber(v) || isBoolean(v) || isDate(v)) {
        // 只要是数字和布尔类型时间就不算空
        return false;
    }
    return lodashIsEmpty(v);
}

/**
 * 清除对象中空数据
 *
 * @export
 * @param {*} o
 * @return {*}
 */
export function clearObject(o: any): any {
    return pickBy(o, (item: any) => {
        return !isEmpty(item);
    });
}

/**
 *
 *
 * @export
 * @param {*} list 原始数据
 * @param {*} config 映射配置
 * @param {*} [options={}] 同getDataByConfig options参数
 * @return {*}  {*} 新数据
 */
export function getListByConfig(
    list: any,
    config: any,
    options: any = {}
): any {
    const data = [];
    list = cloneDeep(list);
    list.forEach((item: any) => {
        item = getDataByConfig(item, config, options);
        if (item) {
            data.push(item);
        }
    });
    // console.log(config);
    return data;
}

/**
 * 根据映射配置获取数据
 * 有一个字段无值则返回null
 * @export
 * @param {*} data 原始数据
 * @param {*} config 映射配置
 * @param {*} [{ isDeep = true, 是否追加默认数据
 *               isDel = false, 是否删除已经重新命名的数据
 *               checkEmpty = false, 是否检查空数据（如果为ture，有一个字段无值则返回null）
 *               numberList = [] 要转换为数字格式的字段
 *               stringList = [] 要转换为字符串格式的字段
 *               appendsData = {} 追加数据
 *            }={}]
 * @return {*}  {*} 新数据
 */
export function getDataByConfig(
    data: any,
    config: any,
    {
        isDeep = true,
        isDel = false,
        checkEmpty = false,
        numberList = [],
        stringList = [],
        appendsData = {}
    }: any = {}
): any {
    const item: any = {};
    const is = true;
    for (const name in config) {
        // 根据映射配置获取值
        const key = config[name];
        let value = get(data, key);
        if (numberList.includes(name)) {
            value = parseFloat(value);
        } else if (stringList.includes(name)) {
            value = value.toString();
        }
        // 如果数据为空
        if (checkEmpty && isEmpty(value)) {
            // 标识并跳出循环
            // is = false;
            // break;
        } else {
            set(item, name, value);
            if (isDel) {
                // 删除已经重新命名的数据
                unset(data, key);
            }
        }
    }
    if (is && isDeep) {
        // 追加未处理数据
        defaults(item, data, appendsData);
    }
    return is ? item : null;
}

/**
 * 正则表达式验证
 *
 * @export
 * @param {*} regex 正则表达式
 * @param {*} value 值
 * @return {*} 是否通过验证
 */
export function regexVerify(regex: any, value: any): any {
    return !isEmpty(value) ? regex.test(value) : true;
}

/**
 * 验证链接
 *
 * @export
 * @param {*} value 值
 * @return {*} 是否通过验证
 */
export function linkVerify(value: any): any {
    return regexVerify(/(http|https):\/\/([\w.]+\/?)\S*/, value);
}

/**
 * 过滤列表数据，只返回指定字段
 *
 * @param {*} list [{a:1,b:2,c:3}]
 * @param {*} keys ['a','b']
 * @returns [{a:1,b:2}]
 */
export function pickList(list: any, keys: any) {
    const data: any = [];
    forEach(list, (item) => {
        data.push(pick(item, keys));
    });
    return data;
}

export function checkCondition({ condition }: any, data: any) {
    // 默认满足
    let is = true;
    if (!isEmpty(condition)) {
        // 遍历条件集合
        forEach(condition, (item) => {
            if (isString(item)) {
                // 如果是字符串，直接根据对应字段的值判断
                is = get(data, item);
            } else if (isFunction(item)) {
                is = item(data);
            } else if (isPlainObject(item)) {
                // 如果是{key:value}类型
                // 循环遍历对比处理（强校验）
                forIn(item, (value, key) => {
                    is = eq(get(data, key), value);
                    // 失败则跳出循环
                    if (!is) {
                        return false;
                    }
                });
            }
            // 失败则跳出循环
            if (!is) {
                return false;
            }
        });
    }
    // 返回校验结果
    return is;
}

export default {};
